package com.boilermaker.lotterysystem.service.impl;

import cn.hutool.crypto.digest.BCrypt;
import com.boilermaker.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.boilermaker.lotterysystem.common.exception.ServiceException;
import com.boilermaker.lotterysystem.common.utils.JWTUtil;
import com.boilermaker.lotterysystem.common.utils.RegexUtil;
import com.boilermaker.lotterysystem.controller.param.UserLoginParam;
import com.boilermaker.lotterysystem.controller.param.UserPasswordLoginParam;
import com.boilermaker.lotterysystem.controller.param.UserRegisterParam;
import com.boilermaker.lotterysystem.controller.param.UserVerificationCodeLoginParam;
import com.boilermaker.lotterysystem.dao.dataobject.UserDO;
import com.boilermaker.lotterysystem.dao.mapper.UserMapper;
import com.boilermaker.lotterysystem.dao.dataobject.Encrypt;
import com.boilermaker.lotterysystem.service.UserService;
import com.boilermaker.lotterysystem.service.VerificationCodeService;
import com.boilermaker.lotterysystem.service.dto.UserInfoDTO;
import com.boilermaker.lotterysystem.service.dto.UserLoginDTO;
import com.boilermaker.lotterysystem.service.dto.UserRegisterDTO;
import com.boilermaker.lotterysystem.service.enums.UserIdentityEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private VerificationCodeService verificationCodeService;

    /**
     * 注册实现
     *
     * @param param
     * @return
     */
    @Override
    public UserRegisterDTO register(UserRegisterParam param) {

        // 校验注册信息
        checkRegisterInfo(param);

        // 构造 UserDO
        UserDO userDO = new UserDO();
        userDO.setUserName(param.getName());
        userDO.setEmail(param.getMail());
        userDO.setPhoneNumber(new Encrypt(param.getPhoneNumber())); // 对称加密
        userDO.setIdentity(param.getIdentity());

        if (StringUtils.hasText(param.getPassword())) { // 仅管理员需要填写密码
            userDO.setPassword(BCrypt.hashpw(param.getPassword())); // 加盐 hash
        }

        // 保存用户数据
        userMapper.insert(userDO);

        // 构造 DTO 用于返回
        UserRegisterDTO dto = new UserRegisterDTO();
        dto.setUserId(userDO.getId());
        return dto;
    }

    /**
     * 登录实现
     *
     * @param param
     * @return
     */
    @Override
    public UserLoginDTO login(UserLoginParam param) {
        UserLoginDTO userLoginDTO;

        // 由于密码登录和验证码登录共用该实现，需要参数对类型检查，以匹配相应的登录逻辑
        if (param instanceof UserPasswordLoginParam loginParam) { // 密码登录
            userLoginDTO = loginByPassword(loginParam);

        } else if (param instanceof UserVerificationCodeLoginParam loginParam) { // 验证码登录
            userLoginDTO = loginByVerificationCode(loginParam);

        } else {
            throw new ServiceException(ServiceErrorCodeConstants.LOGIN_NOT_EXIST);
        }

        return userLoginDTO;
    }

    /**
     * 根据身份查询人员列表
     *
     * @param identity 受管控的身份标识，若为空则不限身份
     * @return 用户列表（全属性）
     */
    @Override
    public List<UserInfoDTO> findUserInfoByIdentity(UserIdentityEnum identity) {

        String identityString = identity == null ? null : identity.name();

        List<UserDO> userDOList = userMapper.findUserListByIdentity(identityString);

        return userDOList.stream()
                .map(userDO -> {
                    UserInfoDTO userDTO = new UserInfoDTO();
                    userDTO.setUserId(userDO.getId());
                    userDTO.setUserName(userDO.getUserName());
                    userDTO.setEmail(userDO.getEmail());
                    userDTO.setPhoneNumber(userDO.getPhoneNumber().getValue());
                    userDTO.setIdentity(UserIdentityEnum.forName(userDO.getIdentity()));
                    return userDTO;
                }).collect(Collectors.toList());
    }

    /**
     * 密码登录
     */
    private UserLoginDTO loginByPassword(UserPasswordLoginParam loginParam) {
        UserDO userDO;

        // 手机或邮箱
        if (RegexUtil.checkMail(loginParam.getLoginName())) { // 邮箱登录
            userDO = userMapper.findForLoginByEmail(loginParam.getLoginName());

        } else if (RegexUtil.checkMobile(loginParam.getLoginName())) { // 手机号码登录
            userDO = userMapper.findForLoginByPhoneNumber(new Encrypt(loginParam.getLoginName()));

        } else {
            throw new ServiceException(ServiceErrorCodeConstants.LOGIN_NAME_NOT_EXIST);
        }

        // 若未查询到该用户
        if (userDO == null) {
            throw new ServiceException(ServiceErrorCodeConstants.USER_INFO_NOT_EXIST);
        }

        // 若有强制登录的限制，但身份校验未通过
        if (StringUtils.hasText(loginParam.getMandatoryIdentity()) &&
                !loginParam.getMandatoryIdentity().equalsIgnoreCase(userDO.getIdentity())) {
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        }

        // 若密码校验未通过
        if (!BCrypt.checkpw(loginParam.getPassword(), userDO.getPassword())) {
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_ERROR);
        }

        // 构造 JWT 令牌
        Map<String, Object> claim = new HashMap<>();
        claim.put("id", userDO.getId());
        claim.put("identity", userDO.getIdentity());
        String jwt = JWTUtil.genJwt(claim);

        // 返回 DTO 响应
        UserLoginDTO userLoginDTO = new UserLoginDTO();
        userLoginDTO.setToken(jwt);
        userLoginDTO.setIdentity(UserIdentityEnum.forName(userDO.getIdentity()));
        return userLoginDTO;
    }

    /**
     * 验证码登录
     */
    private UserLoginDTO loginByVerificationCode(UserVerificationCodeLoginParam loginParam) {
        String loginMail = loginParam.getLoginMail();

        if (!RegexUtil.checkMail(loginMail)) {
            throw new ServiceException(ServiceErrorCodeConstants.MAIL_PATTERN_ERROR);
        }

        UserDO userDO = userMapper.findForLoginByEmail(loginMail);

        if (userDO == null) {
            throw new ServiceException(ServiceErrorCodeConstants.USER_INFO_NOT_EXIST);
        }

        // 若有强制登录的限制，但身份校验未通过
        if (StringUtils.hasText(loginParam.getMandatoryIdentity()) &&
                !loginParam.getMandatoryIdentity().equalsIgnoreCase(userDO.getIdentity())) {
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        }

        String code = verificationCodeService.getVerificationCode(loginMail);

        if (code == null) {
            throw new ServiceException(ServiceErrorCodeConstants.VERIFICATION_CODE_ERROR);
        }

        // 构造 JWT 令牌
        Map<String, Object> claim = new HashMap<>();
        claim.put("id", userDO.getId());
        claim.put("identity", userDO.getIdentity());
        String jwt = JWTUtil.genJwt(claim);

        // 返回 DTO 响应
        UserLoginDTO userLoginDTO = new UserLoginDTO();
        userLoginDTO.setToken(jwt);
        userLoginDTO.setIdentity(UserIdentityEnum.forName(userDO.getIdentity()));
        return userLoginDTO;
    }

    /**
     * 注册信息校验
     */
    private void checkRegisterInfo(UserRegisterParam param) {
        if (param == null) {
            throw new ServiceException(ServiceErrorCodeConstants.REGISTER_INFO_IS_EMPTY);
        }

        // 校验邮箱格式
        if (!RegexUtil.checkMail(param.getMail())) {
            throw new ServiceException(ServiceErrorCodeConstants.MAIL_PATTERN_ERROR);
        }

        // 校验手机号格式
        if (!RegexUtil.checkMobile(param.getPhoneNumber())) {
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_PATTERN_ERROR);
        }

        // 校验身份信息
        if (UserIdentityEnum.forName(param.getIdentity()) == null) {
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        }

        // 若为管理员，则必填密码
        if (param.getIdentity().equalsIgnoreCase(UserIdentityEnum.ADMIN.name())) {

            // 校验密码是否填入
            if (!StringUtils.hasText(param.getPassword())) {
                throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_IS_EMPTY);
            }

            // 校验密码格式
            if (!RegexUtil.checkPassword(param.getPassword())) {
                throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_PATTERN_ERROR);
            }
        }

        // 校验邮箱是否已被注册
        if (checkMailUsed(param.getMail())) {
            throw new ServiceException(ServiceErrorCodeConstants.MAIL_USED);
        }

        // 校验手机号是否已被注册
        if (checkPhoneNumberUsed(param.getPhoneNumber())) {
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_USED);
        }
    }

    private boolean checkPhoneNumberUsed(String phoneNumber) {
        int count = userMapper.countByPhone(new Encrypt(phoneNumber));
        return count > 0;
    }

    private boolean checkMailUsed(String mail) {
        int count = userMapper.countByMail(mail);
        return count > 0;
    }
}
